package com.hbuzzs.springcloud.conf;

import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @Description TODO
 * @Author 张智顺
 * @Date 2020/8/30-23:10
 */
@Aspect
@Component
public class WebLogAspect {


    private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

    /**
     * 以 controller 包下定义的所有请求为切入点
     * 扫描所有接口： @Pointcut("execution(public * com....controller..*.*(..))")
     */
    @Pointcut("@annotation(com.hbuzzs.springcloud.conf.LogAnnotation)")
    public void webLog() {
    }

    /**
     * 在切点之前织入
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String logAnnoVal = getLogAnnoVal(joinPoint);
        // 打印 Http method
        // 打印调用 controller 的全路径以及执行方法
        logger.info("[{}] 请求方法 \t: {}, 接口位置 \t: {}.{}", logAnnoVal, request.getMethod(), joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        // 打印请求入参
        try {
            Object requestParam = joinPoint.getArgs();
            logger.info("[{}] 参数 \t: {}", logAnnoVal, JSONObject.toJSONString(requestParam));
        } catch (Exception e) {
            logger.info("[{}] 参数打印失败，异常 \t: {}", logAnnoVal, e.getMessage());
        }
    }

    /**
     * 获取接口名字或URI地址
     */
    private String getLogAnnoVal(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
        // 如果注解有写value就是用注解的值，否则使用URI值
        return "".equals(annotation.value()) ? request.getRequestURI() : annotation.value();
    }

    /**
     * 在切点之后织入
     */
    @After("webLog()")
    public void doAfter() {
    }

    /**
     * 环绕
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 开始打印请求日志
        String logAnnoVal = getLogAnnoVal(proceedingJoinPoint);
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
        logger.info("[{}] 耗时: {} ms , 返回值 : {}", logAnnoVal, System.currentTimeMillis() - startTime, result!=null?JSONObject.toJSONString(result):null);
        return result;
    }

}
